<template>
<div>
    <ph-layer id="ph-colorpicker" :visible="visible" @click="onMaskClick" class="ph-color-layer">
    </ph-layer>
    <action-sheet class="ph-color-panel" :visible="visible" v-if="isMobile">
        <sv :h="hsb[0]" :s="hsb[1]" :b="hsb[2]" @pick="onPick" :isMobile="isMobile"/>
        <hac :alpha="alpha" :hua="hsb[0]" :value="hex" @hue="onHue" @alpha="onAlpha"/>
        <cinput :value="modeValue" :mode="mode" @mode-shift="onModeShift" @color-change="onColorInput"/>
        <predef v-if="state.preshow" @pick="onPrecolor" :precolors="state.precolors"/>
    </action-sheet>
    <template v-else >
        <transition name="ph-fade">
        <div class="ph-color-panel" v-show="visible" v-ph-position:v="{visible,...position}" @click.stop ref="ecolorPanel">
            <sv :h="hsb[0]" :s="hsb[1]" :b="hsb[2]" :isMobile="isMobile" @pick="onPick"/>
            <hac :alpha="alpha" :hua="hsb[0]" :value="hex" @hue="onHue" @alpha="onAlpha"/>
            <cinput :value="modeValue" :mode="mode" @mode-shift="onModeShift" @color-change="onColorInput"/>
            <predef v-if="state.preshow" @pick="onPrecolor" :precolors="state.precolors"/>
        </div>
        </transition>
    </template>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, ref, defineAsyncComponent, onBeforeUnmount, PropType, reactive, toRefs } from 'vue'
import { PhLayer } from '../base'
import { ColorMode 
, parseColor 
, rgb2hex 
, rgb2hsl 
, hsl2rgb
, hsla2hex 
, createRgba 
, createHsla 
, rgb2hsb 
, hsb2rgb
, hsb2hex
} from 'ph-color-util'
import Evt from 'ph-evt'
import sv from './sv.vue'
import hac from './hac.vue'
import cinput from './input.vue'
import predef from './predef.vue'
import {PhPosition} from '../../directives'
import {ColorPredefine} from '../../shared/config'
import {PhColorpickerOpt} from './index.type'
import {checkMobile} from '../../shared/util'
import MatchMedia from 'ph-media'
export default defineComponent({
    directives:{PhPosition},
    components:{
        PhLayer,
        sv,
        hac,
        cinput,
        predef,
        ActionSheet:defineAsyncComponent(()=>import('../actionsheet/main.vue'))
    },
    props:{
        preshow:{
            type:Boolean,
            default:true
        },
        precolors:{
            type:Array as PropType<Array<string>>,
            default:ColorPredefine
        }
    },
    setup(props) {
        const state = reactive<{
            preshow:boolean,
            precolors:string[],
            [k:string]:unknown
        }>({
            preshow:props.preshow,
            precolors:props.precolors
        })
        const ecolorPanel = ref<HTMLElement>()
        const color = ref("#FFFFFFFE")
        const visible = ref(false)
        const $evt = new Evt()
        const position = {
            left:0,
            top:0,
            height:0
        } as Record<string,number>
        const isMobile = ref(false)
        const {preshow,precolors} = toRefs(state)
        
        const Color = computed(()=>{
            return parseColor(color.value);
        })
        const mode = computed(()=>{
            return Color.value.mode
        })
        const RGBA = computed(()=>{
            return (Color.value.rgba as number[])||[0,0,0,0]
        })
        const alpha = computed(()=>{
            const a = RGBA.value[3]
            return a===undefined?1:a
        })
        const hex = computed(()=>{
            if(mode.value===ColorMode.hex)return color.value
            return rgb2hex(RGBA.value[0],RGBA.value[1],RGBA.value[2],RGBA.value[3])
        })
        const rgba = computed(()=>{
            return RGBA.value
        })
        const hsla = computed(()=>{
            const hsl = rgb2hsl(RGBA.value[0],RGBA.value[1],RGBA.value[2]);
            return [...hsl,RGBA.value[3]] as number[]
        })
        const hsb = computed(()=>{
            return rgb2hsb(RGBA.value[0],RGBA.value[1],RGBA.value[2])
        })
        const modeValue = computed(()=>{
            switch(mode.value){
                case ColorMode.rgb:
                case ColorMode.rgba:
                    return rgba.value
                case ColorMode.hsl:
                case ColorMode.hsla:
                    return hsla.value
                default:
                    return hex.value
            }
        })
        const notify = (v:string)=>{
            color.value = v
            $evt.emit("pick",v);
        }
        const onPick = ([h,s,v]:[number,number,number])=>{
            const [a,b,c] = hsb.value
            if(a===h&&b===s&&c===v)return
            switch(mode.value){
                case ColorMode.hex:
                    notify(hsb2hex(h,s,v,alpha.value))
                    break
                case ColorMode.rgba:{
                    const [r,g,b] = hsb2rgb(h,s,v);
                    notify(createRgba(r,g,b,alpha.value))
                    break
                }
                case ColorMode.hsla:{
                    const [r,g,b] = hsb2rgb(h,s,v);
                    const [h1,s1,l] = rgb2hsl(r,g,b)
                    notify(createHsla(h1,s1,l,alpha.value))
                    break
                }
            }
        }
        const onHue = (h:number)=>{
            if(h===hsb.value[0])return
            switch(mode.value){
                case ColorMode.hex:
                    notify(hsla2hex(h,hsla.value[1],hsla.value[2],rgba.value[3]))
                    break
                case ColorMode.rgba:{
                    const [r,g,b] = hsl2rgb(h,hsla.value[1],hsla.value[2])
                    notify(createRgba(r,g,b,alpha.value))
                    break;
                }
                case ColorMode.hsla:{
                    notify(createHsla(h,hsla.value[1],hsla.value[2],alpha.value))
                    break;
                }
            }
        }
        const onAlpha = (a:number)=>{
            if(a===alpha.value)return
            switch(mode.value){
                case ColorMode.hex:
                    notify(rgb2hex(rgba.value[0],rgba.value[1],rgba.value[2],a))
                    break
                case ColorMode.rgba:
                    notify(createRgba(rgba.value[0],rgba.value[1],rgba.value[2],a))
                    break
                case ColorMode.hsla:
                    notify(createHsla(hsla.value[0],hsla.value[1],hsla.value[2],a))
                    break
            }
        }
        const onModeShift = (m:ColorMode)=>{
            switch(m){
                case ColorMode.rgba:
                    notify(createRgba(rgba.value[0],rgba.value[1],rgba.value[2],alpha.value))
                    break
                case ColorMode.hsla:
                    notify(createHsla(hsla.value[0],hsla.value[1],hsla.value[2],alpha.value))
                    break
                default:
                notify(hex.value)
            }
        }
        const onColorInput = ({name,value}:{name:string,value:string})=>{
            switch(name){
                case "hex":
                    if([4,7,9].includes(value.length))
                        notify(value)
                    break
                case "R":{
                    const r = Number(value)
                    if(r>=0&&r<=255)
                        notify(createRgba(r,rgba.value[1],rgba.value[2],alpha.value))
                    break;
                    }
                case "G":{
                    const g = Number(value)
                    if(g>=0&&g<=255)
                        notify(createRgba(rgba.value[0],g,rgba.value[2],alpha.value))
                    break;
                    }
                case "B":{
                    const b = Number(value)
                    if(b>=0&&b<=255)
                        notify(createRgba(rgba.value[0],rgba.value[1],b,alpha.value))
                    break;
                    }
                case "H":{
                    const h = Number(value)
                    if(h>=0&&h<=360)
                        notify(createHsla(h,hsla.value[1],hsla.value[2],alpha.value))
                    break;
                    }
                case "S":{
                    const s = parseInt(value)
                    if(s>=0&&s<=100)
                        notify(createHsla(hsla.value[0],s/100,hsla.value[2],alpha.value))
                    break;
                    }
                case "L":{
                    const l = parseInt(value)
                    if(l>=0&&l<=100)
                        notify(createHsla(hsla.value[0],hsla.value[1],l/100,alpha.value))
                    break;
                    }
                case "A":{
                    const a = Number(value)
                    if(a>=0&&a<=255)
                        onAlpha(a)
                    break;
                    }
            }
        }
        let restoreState : ()=>void;
        const restore = ()=>{
            setTimeout(()=>{
                restoreState&&restoreState()
            },300)
        }
        
        const cachePosition = ({left,top,height}:PhColorpickerOpt)=>{
            Object.assign(position,{left,top,height})
        }
        const onPrecolor = (pc:string)=>{
            if(pc===color.value)return
            notify(pc)
        }
        const onMaskClick = ()=>{
            visible.value = false
            restore()
            $evt.off("pick")
        }
        const onPickDone = (fn:()=>void)=>{
            $evt.off("pick")
            $evt.on("pick",fn)
        }
        
        const save = (opt:PhColorpickerOpt)=>{
            const oldState = {...state}
            Object.keys(oldState).forEach(k=>{
                if(k in opt&&opt[k]!=undefined)
                    state[k] = opt[k]
            })
            restoreState = ()=>{
                Object.keys(oldState).forEach(k=>{
                    state[k] = oldState[k]
                })
            }
            cachePosition(opt)
        }
        const show = (c:string,opt:PhColorpickerOpt)=>{
            visible.value = true
            if(c)color.value = c
            save(opt)
            return {
                done:onPickDone
            }
        }
        const mediaListener = (matches:boolean)=>{
			isMobile.value = matches
		}
        
        MatchMedia.maxWidth("768px",mediaListener)
        onBeforeUnmount(()=>{
            MatchMedia.offMaxWidth("768px",mediaListener)
        })
        const resize = ()=>{
            if(!isMobile.value)
			isMobile.value = checkMobile()
		}
		window.onresize = resize
		resize()
        return {
            isMobile,
            ecolorPanel,
            visible,
            position,
            state,
            color,
            mode,
            hex,
            onPick,
            onHue,
            onAlpha,
            modeValue,
            onModeShift,
            onColorInput,
            onPrecolor,
            onMaskClick,
            show,
            hsb,
            alpha,
        }
    },
    
})
</script>
<style lang="scss" scoped>
.ph-color-layer{
    justify-content: flex-start;
    align-items: flex-start;
    overflow: auto;
}
.ph-color-panel{
    background-color: #fff;
    overflow: hidden;
    backface-visibility: hidden;
    z-index: 15000;
}
@media screen and (max-width: 768px) {
    .ph-color-panel{
        box-shadow:0px -va(--ph-5) var(--ph-15) rgb(0 0 0 / 40%);
    }
}
@media screen and (min-width: 769px) {
    .ph-color-panel{
        box-shadow: 1px 1px var(--phx) rgb(0 0 0 / 30%);
        position: absolute;
        left: 0;
        top: 0;
        width: calc(var(--phx) * 17.8566);
    }
}
</style>